package com.basker.pisces.trace;

import java.util.Stack;

import org.springframework.util.Assert;

import com.basker.pisces.spring.SpringApplicationContext;
import com.basker.pisces.trace.annotation.EnableTrace.TraceEvent;
import com.basker.pisces.trace.event.SpanEvent;
import com.basker.pisces.utils.EnumUtils;
import com.basker.pisces.utils.GUIDUtils;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class TraceContext {

    private Stack<Span> spanList = new Stack<>();

    /**
     * 在当前的span下创建一个子span
     *
     * @return
     */
    public Span createChildSpan() {
        Assert.isTrue(!this.spanList.isEmpty(), "TraceContext' has no root span!");

        Span parent = currentSpan();
        Span child = new Span();

        child.setTraceId(parent.getTraceId());
        child.setId(GUIDUtils.generateGUID());
        child.setParentId(parent.getId());
        child.setStart(System.currentTimeMillis());

        this.spanList.add(child);

        return child;
    }

    /**
     * 创建根span
     *
     * @return
     */
    public Span createRootSpan() {
        return createRootSpan(null);
    }

    /**
     * 从请求上下文获取调用者的span，如果有的话，以它为基础创建根span
     *
     * @param spanFromRequest
     * @return
     */
    public Span createRootSpan(Span spanFromRequest) {
        Assert.isTrue(this.spanList.isEmpty(), "TraceContext' exist root span!");

        Span root = new Span();

        if (spanFromRequest != null) {
            root.setTraceId(spanFromRequest.getTraceId());
            root.setId(spanFromRequest.getId());
            root.setParentId(spanFromRequest.getParentId());
        } else {
            root.setTraceId(GUIDUtils.generateGUID());
            root.setId(GUIDUtils.generateGUID());
        }

        root.setStart(System.currentTimeMillis());

        this.spanList.add(root);

        return root;
    }

    /**
     * 获取当前的span
     *
     * @return
     */
    public Span currentSpan() {
        if (!this.isEmpty()) {
            return spanList.peek();
        }

        return null;
    }

    /**
     * 获取traceId
     *
     * @return
     */
    public String getTraceId() {
        if (!this.isEmpty()) {
            return currentSpan().getTraceId();
        }

        return null;
    }

    public boolean isEmpty() {
        return spanList.isEmpty();
    }

    /**
     * 当前上下文内，是否与指定的跟踪事件匹配
     *
     * @param event
     * @return
     */
    public boolean matchEvent(TraceEvent event) {
        if (this.isEmpty()) {
            return false;
        }

        return spanList.stream().anyMatch(span -> EnumUtils.isMatchEnum(span.getTraceEvents(), event));
    }

    public void publishSpanCreateEvent(Span span) {
        SpringApplicationContext.getInstance().publishEvent(new SpanEvent.CreateEvent(this, span));
    }

    public void publishSpanDestoryEvent(Span span) {
        SpringApplicationContext.getInstance().publishEvent(new SpanEvent.DestoryEvent(this, span));
    }

    /**
     * 移除当前span
     *
     * @return
     */
    public Span removeCurrentSpan() {
        if (!this.isEmpty()) {
            Span span = spanList.pop();

            if (span != null) {
                span.setEnd(System.currentTimeMillis());
            }

            return span;
        }

        return null;
    }

}
